/**
 * 210811 created by jordia
 * 210817:对外暴露原生合成器实例（entity），以便对渲染结果和各个通道进行操作
 * 20220304:[require] to [import]...jordia
 */
import {Vector2,Color} from 'three';
import {EffectComposer} from "three/examples/jsm/postprocessing/EffectComposer";
import {RenderPass} from "three/examples/jsm/postprocessing/RenderPass";
import {OutlinePass} from "three/examples/jsm/postprocessing/OutlinePass";
import {SSAARenderPass} from "three/examples/jsm/postprocessing/SSAARenderPass"

/**
 * 轮廓高亮效果
 * @param camera {Camera} 相机
 * @param renderer {WebGLRenderer} 渲染器
 * @param scene {Scene} 场景
 * @param values {Object} 可选参数对象：
 *      antialias {number} SSAA抗锯齿级别（0为关闭抗锯齿，抗锯齿非常耗性能，无特别需求尽量不要开启）：undefined
 *      edgeThickness {number} 轮廓厚度:1
 *      edgeStrength {number} 轮廓强度:3
 *      edgeGlow {number} 轮廓辉光大小:0
 *      downSampleRatio {number} 采样率:2
 *      usePatternTexture {boolean} 使用遮罩纹理：false
 *      patternTexture {texture|null} 遮罩纹理:null
 *      visibleEdgeColor {color} 可见部分颜色：new Color(1,1,1)
 *      hiddenEdgeColor {color} 遮挡部分颜色：new Color( 0.1, 0.04, 0.02 )
 *      pulsePeriod {number} 闪烁间隔：0（无闪烁）
 *
 * 示例：
    let outline=new OutlineSelective(camera,renderer,scene,{
        edgeStrength:3,
        edgeThickness:4,
        edgeGlow:3,
        downSampleRatio:1,
        visibleEdgeColor:new Color('#04ff00'),
        hiddenEdgeColor:new Color('#ff0100'),
        pulsePeriod:0.8,
        usePatternTexture:true,
        patternTexture:tex,
        antialias:1,
    });
    outline.select(meshes[0]);
    let t=0;
    setInterval(()=>{
        outline.unselect(meshes[t]);
        t++;
        t%=10;
        outline.select(meshes[t]);
    },2000);
    setTimeout(()=>{
        outline.visibleEdgeColor=new Color('#00c4ff');
    },5000);
 */
export default class OutlineSelective {
    constructor(camera,renderer,scene,values={}){
        this.cam=camera;
        this.rdr=renderer;
        this.scn=scene;
        this.values=values;
        this.init();
    }

    //....................................public........................................

    get entity(){
        return this.outlineComp;
    }

    set edgeThickness(num){
        this.values.edgeThickness=Math.max(num,0);
        this.outlinePas.edgeThickness=this.values.edgeThickness;
    }
    get edgeThickness(){
        return this.values.edgeThickness;
    }

    set edgeStrength(num){
        this.values.edgeStrength=Math.max(num,0);
        this.outlinePas.edgeStrength=this.values.edgeStrength;
    }
    get edgeStrength(){
        return this.values.edgeStrength;
    }

    set edgeGlow(num){
        this.values.edgeGlow=Math.max(num,0);
        this.outlinePas.edgeGlow=this.values.edgeGlow;
    }
    get edgeGlow(){
        return this.values.edgeGlow;
    }

    set downSampleRatio(num){
        this.values.downSampleRatio=Math.max(num,0);
        this.outlinePas.downSampleRatio=this.values.downSampleRatio;
        this.setSize(this.tSize.x,this.tSize.y);
    }
    get downSampleRatio(){
        return this.values.downSampleRatio;
    }

    set usePatternTexture(bol){
        this.values.usePatternTexture=bol;
        this.outlinePas.usePatternTexture=this.values.usePatternTexture;
    }
    get usePatternTexture(){
        return this.values.usePatternTexture;
    }

    set patternTexture(texture){
        this.values.patternTexture=texture;
        this.outlinePas.patternTexture=this.values.patternTexture;
    }
    get patternTexture(){
        return this.values.patternTexture;
    }

    set visibleEdgeColor(color){
        this.values.visibleEdgeColor.copy(color);
        this.outlinePas.visibleEdgeColor.copy(this.values.visibleEdgeColor);
    }
    get visibleEdgeColor(){
        return this.values.visibleEdgeColor;
    }

    set hiddenEdgeColor(color){
        this.values.hiddenEdgeColor.copy(color);
        this.outlinePas.hiddenEdgeColor.copy(this.values.hiddenEdgeColor);
    }
    get hiddenEdgeColor(){
        return this.values.hiddenEdgeColor;
    }

    set pulsePeriod(num){
        this.values.pulsePeriod=Math.max(num,0);
        this.outlinePas.pulsePeriod=this.values.pulsePeriod;
    }
    get pulsePeriod(){
        return this.values.pulsePeriod;
    }

    /**
     * 渲染前执行函数
     * @param func
     */
    set onBeforeRender(func){
        this.beforeRender=func;
    }

    /**
     * 渲染后执行函数
     * @param func
     */
    set onAfterRender(func){
        this.afterRender=func;
    }

    /**
     * 选择对象（显示轮廓）
     * @param objs
     */
    select(...objs){
        for(let obj of objs){
            if(this.selected.findIndex((o)=>o.uuid===obj.uuid)<0){
                this.selected.push(obj);
            }
        }
    }

    /**
     * 取消选择对象（不显示轮廓）
     * @param objs
     */
    unselect(...objs){
        for(let obj of objs){
            let idx=this.selected.findIndex((o)=>o.uuid===obj.uuid);
            if(idx<0)continue;
            this.selected.splice(idx,1);
        }
    }

    /**
     * 清除选择对象
     */
    clear(){
        this.unselect(...this.selected);
    }

    /**
     * 设置合成器尺寸
     * @param width
     * @param height
     */
    setSize(width,height){
        this.tSize.set(width,height);
        this.outlineComp.setSize(width,height);
        this.render();
    }

    /**
     * 渲染函数
     */
    render(){
        if(this.beforeRender)this.beforeRender();
        this.outlineComp.render();
        if(this.afterRender)this.afterRender();
    }

    //.....................................private.......................................

    init(){
        this.initVariable();
        this.initComponents();
    }
    initVariable(){
        this.outlineComp=null;
        this.scenePas=null;
        this.outlinePas=null;
        this.selected=[];
        this.beforeRender=null;
        this.afterRender=null;
        this.tSize=new Vector2();
    }
    initComponents(){
        this.initOutlineComposer();
        this.initializeOutline();
    }
    initOutlineComposer(){
        this.tSize=new Vector2();
        this.rdr.getSize(this.tSize);//composer内部会乘上像素比，此处不能乘以像素比，否则移动端无法渲染
        this.outlineComp=new EffectComposer(this.rdr);
        if(this.values.antialias){
            this.scenePas=new SSAARenderPass(this.scn,this.cam);
            this.scenePas.sampleLevel=this.values.antialias;
        }else{
            this.scenePas=new RenderPass(this.scn,this.cam);
        }
        this.outlineComp.addPass(this.scenePas);
        this.outlinePas=new OutlinePass(this.tSize,this.scn,this.cam,this.selected);
        this.outlineComp.addPass(this.outlinePas);
    }
    initializeOutline(){
        let v=this.values;
        this.edgeThickness=v.edgeThickness=v.edgeThickness!==undefined?v.edgeThickness:1;
        this.edgeStrength=v.edgeStrength=v.edgeStrength!==undefined?v.edgeStrength:3;
        this.edgeGlow=v.edgeGlow=v.edgeGlow||0;
        this.usePatternTexture=v.usePatternTexture=v.usePatternTexture||false;
        this.patternTexture=v.patternTexture=v.patternTexture||null;
        v.visibleEdgeColor=v.visibleEdgeColor||new Color(1,1,1);
        this.visibleEdgeColor=v.visibleEdgeColor;
        v.hiddenEdgeColor=v.hiddenEdgeColor||new Color( 0.1, 0.04, 0.02 );
        this.hiddenEdgeColor=v.hiddenEdgeColor;
        this.pulsePeriod=v.pulsePeriod=v.pulsePeriod||0;
        this.downSampleRatio=v.downSampleRatio=v.downSampleRatio||2;
    }
}